#include <string.h>

/*
  class CHARACTER
  ===============
  A minimal class used when passing string arguments from C++
  to FORTRAN 77 (received as FORTRAN 77 CHARACTER strings), and
  subsequently returned back to C++ as properly zero terminated
  strings.

  Method used for zero-termination:
  =================================
  When the CHARACTER destructor is activated the zero-termination
  of the c-string is automatically managed. Zero termination is
  also done each time a string array is subscripted using
  CHARACTER::operator()(size_t index)

  FORTRAN Assumptions:
  ====================
  (1) F77 truncates strings when CHARACTER variable is short
  (2) F77 pads variable with blanks when assigned string is short
  (3) F77 represents a string as a pointer followed by a length
  (4) A string array is stored in contiguous memory

  Author: Carsten A. Arnholm, 20-AUG-1995

  Updates:
      04-MAR-1996 Added features for handling arrays of strings
      16-MAR-1996 Tested array features, explicit padding included
      29-JUL-1996 Tested portability to SGI/Unix, moved decl. of destructor
      04-APR-1997 Using strncpy instead of strcpy in operator=(char* str);
*/

class CHARACTER {
public:
  CHARACTER(char *cstring);
  CHARACTER(char *cstring, const size_t lstr);
  ~CHARACTER();
  CHARACTER operator()(size_t index);
  void pad(size_t first, size_t howmany = 1);
  void operator=(char *str);
  operator char *();

public:
  char *rep;  // Actual string
  size_t len; // String length
};

inline CHARACTER::CHARACTER(char *cstring)
    : rep(cstring), len(strlen(cstring)){};

inline CHARACTER::CHARACTER(char *cstring, const size_t lstr)
    : rep(cstring), len(lstr) {
  // find position from where to start padding
  size_t slen = strlen(rep);                 // upper limit
  size_t actual = (slen < len) ? slen : len; // actual <= len.
  for (size_t i = actual; i < len; i++)
    rep[i] = ' '; // Do the padding.
}

inline CHARACTER::~CHARACTER() {
  if (rep[len] == '\0')
    return; // catches string constants

  for (int i = len - 1; i >= 0; i--) {
    if (rep[i] == '\0')
      break; // already zero terminated

    if (rep[i] != ' ') { // non-blank discovered, so
      rep[i + 1] = '\0'; // zero-terminate and jump out
      break;
    }
  }
}

inline CHARACTER CHARACTER::operator()(size_t index) {
  // Construct a temporary CHARACTER object for the array element
  // identified by "index" in order to zero-terminate that element
  size_t pos = index * len;          // start pos of array element
  CHARACTER element(rep + pos, len); // construct new CHARACTER.
  return element;                    // destructor called here.
}

inline void CHARACTER::pad(size_t first, size_t howmany) {

  size_t pos = 0, i = 0, stop = first + howmany - 1;
  for (size_t index = first; index <= stop; index++) {
    pos = index * len;
    size_t slen = strlen(rep + pos); // upper limit
    size_t actual = (slen < len) ? slen : len;
    for (i = pos + actual; i < pos + len; i++)
      rep[i] = ' '; // Do the padding.
  }
}

inline void CHARACTER::operator=(char *str) {
  strncpy(rep, str, len);    // this will copy a zero if str < rep
  rep[len - 1] = '\0';       // zero terminate in case strncpy did not
  size_t slen = strlen(rep); // upper limit
  size_t actual = (slen < len) ? slen : len; // actual <= len.
  for (size_t i = actual; i < len; i++)
    rep[i] = ' '; // Do the padding.
}

inline CHARACTER::operator char *() { return rep; }
